﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Configuration;
using System.IO;
using System.Threading;
using System.Collections.Generic;
using XmlServer.ServiceContracts;
using XmlServer.ServiceImplementation;

namespace UnitTests
{
    [TestClass]
    public class StressTest
    {

        
        private int clientErrorCounts = 0;  // used to keep count of the errors
        private int clientsAliveCount = 0;  // the number of clients alive making requests
        List<String> uniqueErrors = new List<string>();
        
        Random rnd = new Random();


        // Enter in the location of the XmlServer (Consol Host or Windows Service)
        private string serverIpAddress = "192.168.0.9";
        private int serverPort = 8095;
        

        /////////////////////////////////////////////////////////
        // Comment out the type of test you want to run
        /////////////////////////////////////////////////////////

        #region Small Test, total requests = 10

        // Recommend starting the XmlService with 5 processing threads
        /*
        private int numberOfClient = 5;            // simulate the number of con-current clients making requests
        private int numberOfRequestsPerClient = 2; // how many requests made be a single client
        private int maxDelaySeconds = 5;           // the maximum number of seconds for random delay requests
        private int secondsBetweenRequests = 2;     // the maximum number of seconds between requests        
        */
        #endregion

        #region Medium Test 1, total requests = 300

        // Recommend starting the XmlService with 20 processing threads
        /*
        private int numberOfClient = 30;            // simulate the number of con-current clients making requests
        private int numberOfRequestsPerClient = 10; // how many requests made be a single client
        private int maxDelaySeconds = 5;           // the maximum number of seconds for random delay requests
        private int secondsBetweenRequests = 5;     // the maximum number of seconds between requests        
        */
        #endregion


        #region Large Test

        // Recommend starting the XmlService with 100 processing threads        
        private int numberOfClient = 100;            // simulate the number of con-current clients making requests
        private int numberOfRequestsPerClient = 10; // how many requests made be a single client
        private int maxDelaySeconds = 4;           // the maximum number of seconds for random delay requests
        private int secondsBetweenRequests = 8;     // the maximum number of seconds between requests        
        
        #endregion


        [Owner("Stress Test"), TestMethod]
        public void StartStressTest()
        {
            int stressStart = System.Environment.TickCount;

            // prepare a status requests
            string statusRequest = XmlServer.Helper.Utilities.CreateRequest("Status", "1.0", null);

            // create the threads to simulate the client requests
            Thread[] clients = new Thread[numberOfClient];
            for (int i = 0; i < numberOfClient; i++ )
            {
                clients[i] = new Thread(new ThreadStart(MakeRequestsFromClients));
                clients[i].Start();

            }

            // calculate the maxium time
            var maxTime = numberOfClient * numberOfRequestsPerClient * maxDelaySeconds * secondsBetweenRequests;

            int startTick = System.Environment.TickCount;
            while (clientsAliveCount > 0)
            {
                // sleep the test thread untill all the clients have dies
                Thread.Sleep(1000);

                // make a priority status requests

                // write out debug info
                int totalTime = System.Environment.TickCount - startTick;
                if (totalTime > 10000)
                {
                    // write out debug messages every X seconds
                    System.Diagnostics.Debug.WriteLine(String.Format("Number of clients: {0}", numberOfClient));
                    System.Diagnostics.Debug.WriteLine(String.Format("Number of errors: {0}", clientErrorCounts));
                }

            }


            // make a final status requests
            System.Diagnostics.Debug.WriteLine("----------------------------------------------------------");
            System.Diagnostics.Debug.WriteLine("FINAL");
            System.Diagnostics.Debug.WriteLine("----------------------------------------------------------");
            System.Diagnostics.Debug.WriteLine(String.Format("Number of errors: {0}", clientErrorCounts));
            System.Diagnostics.Debug.WriteLine("----------------------------------------------------------");
            System.Diagnostics.Debug.WriteLine("----------------------------------------------------------");


            int stressFinish = System.Environment.TickCount;
            System.Diagnostics.Debug.WriteLine("Stress Test Time: {0}", stressFinish - stressStart);

            System.Diagnostics.Debug.WriteLine("----------------------------------------------------------");
            System.Diagnostics.Debug.WriteLine("Unique Errors");
            System.Diagnostics.Debug.WriteLine("----------------------------------------------------------");
            foreach (var item in uniqueErrors)
            {
                System.Diagnostics.Debug.WriteLine(item);
            }

            System.Diagnostics.Debug.WriteLine("FINISHED");

        }

        private void IncrementClientsAliveCount()
        {
            try
            {
                Monitor.Enter(this);
                clientsAliveCount++;
            }
            catch { }
            finally
            {
                Monitor.Exit(this);
            }
        }

        private void DecrementClientsAliveCount()
        {
            try
            {
                Monitor.Enter(this);
                clientsAliveCount--;
            }
            catch { }
            finally
            {
                Monitor.Exit(this);
            }
        }


        private void IncrementClientErrorCount()
        {
            try
            {
                Monitor.Enter(this);
                clientErrorCounts++;
            }
            catch { }
            finally
            {
                Monitor.Exit(this);
            }
        }

        private void AddErrorMessage(string ErrorMsg)
        {
            try
            {
                Monitor.Enter(this);
                if (!uniqueErrors.Contains(ErrorMsg)) uniqueErrors.Add(ErrorMsg);
            }
            catch { }
            finally
            {
                Monitor.Exit(this);
            }
            

        }


        private void MakeRequestsFromClients()
        {
            // a new client starting requests
            IncrementClientsAliveCount();

            for (int i = 0; i < numberOfRequestsPerClient; i++)
            {                
                try
                {
                    // get a random number for the delay
                    var delayOnServer = rnd.Next(this.maxDelaySeconds);

                    // get the request string
                    string request = XmlServer.Helper.Utilities.CreateRequest("Delay Operator", "1.0",
                        new List<IFact>
                        {
                            new Fact { Name = "Delay", Value = delayOnServer.ToString() } ,
                            new Fact { Name = "delayType", Value = "secs"}                    
                        });


                    // send the request and get the response
                    var response = XmlServer.Helper.Utilities.MakeRequest(request, serverIpAddress, serverPort);

                    // parse the response
                    var successResponse = XmlServer.Helper.Utilities.IsResponseSuccess(response);
                    var data = XmlServer.Helper.Utilities.GetFacts(response, true);
                    if (!successResponse)
                    {
                        IncrementClientErrorCount();
                        AddErrorMessage("Server: " + XmlServer.Helper.Utilities.GetFact<string>(data, "ErrorMessage1"));
                    }

                }
                catch (Exception err)
                {                    
                    IncrementClientErrorCount();
                    AddErrorMessage("Client: " + err.Message);
                }
                finally
                {
                    var requestDelay = rnd.Next(this.secondsBetweenRequests * 1000);

                    // sleep the client thread
                    Thread.Sleep(requestDelay);
                }
                
            }

            // a client is finished requests
            DecrementClientsAliveCount();


        }


        private T GetAppSetting<T>(string Key)
        {
            if (String.IsNullOrEmpty(Key)) return default(T);

            var value = ConfigurationManager.AppSettings[Key];

            T castedValue = (T)Convert.ChangeType(value, typeof(T));

            return castedValue;

        }

    }
}
